package com.zhou.designParten.demo1;


import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 *  交替打印FooBar
 *  方式一 LockSupport+concurrentHashMap
 */
class FooBar {
    private int n;
    private final AtomicBoolean atomicBoolean = new AtomicBoolean(true);
    private final ConcurrentHashMap<String,Thread> concurrentHashMap = new ConcurrentHashMap<>();

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        concurrentHashMap.put("foo",Thread.currentThread());
        for (int i = 0; i < n; i++) {
            //如果是false,自旋阻塞
            while(!atomicBoolean.get()){
                LockSupport.park();
            }
        	printFoo.run();
            //准备执行bar
            atomicBoolean.compareAndSet(true,false);
            LockSupport.unpark(concurrentHashMap.get("bar"));
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        concurrentHashMap.put("bar",Thread.currentThread());
        for (int i = 0; i < n; i++) {
            while(atomicBoolean.get()){
                LockSupport.park();
            }
        	printBar.run();
            //准备执行foo
            atomicBoolean.compareAndSet(false,true);
            LockSupport.unpark(concurrentHashMap.get("foo"));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        FooBar fooBar = new FooBar(3);
        Thread thread = new Thread(()-> {
            try {
                fooBar.foo(()-> System.out.print("foo"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread1 = new Thread(()-> {
            try {
                fooBar.bar(()-> System.out.print("bar"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread1.start();
    }
}


/**
 * 方式二：直接使用阻塞队列
 */
class FooBar1{
    private int n;
    private BlockingQueue<Integer> fooQueue = new LinkedBlockingQueue<>(1);//长度为1
    private BlockingQueue<Integer> barQueue = new LinkedBlockingQueue<>(1);//长度为1

    public FooBar1(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            //当队列长度为1即maxSize的时候会一直阻塞
            fooQueue.put(i);
            printFoo.run();
            barQueue.put(i);
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            //长度为0的时候会一直阻塞
            barQueue.take();
            printBar.run();
            fooQueue.take();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        FooBar1 fooBar1 = new FooBar1(4);
        Thread thread = new Thread(()-> {
            try {
                fooBar1.foo(()-> System.out.print("foo"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread1 = new Thread(()-> {
            try {
                fooBar1.bar(()-> System.out.print("bar"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread1.start();
    }
}


/**
 * 方式三：可重入锁+condition
 */
class FooBar2{
    private int n;
    Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    volatile boolean type = true;

    public FooBar2(int n) {
        this.n = n;
    }

    public  void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            lock.lock();
            try{
                if(!type){
                    //线程阻塞
                    condition.await();
                }
                printFoo.run();
                type= false;
                //线程唤醒
                condition.signal();
            }finally {
                lock.unlock();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            lock.lock();
            try{
                if(type){
                    //线程阻塞
                    condition.await();
                }
                printBar.run();
                type= true;
                //线程唤醒
                condition.signal();
            }finally {
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        FooBar2 fooBar2 = new FooBar2(4);
        Thread thread = new Thread(()-> {
            try {
                fooBar2.foo(()-> System.out.print("foo"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread1 = new Thread(()-> {
            try {
                fooBar2.bar(()-> System.out.print("bar"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread1.start();
    }
}

/**
 * 方式四：synchronized+标志位+唤醒
 */
class FooBar3{
    private int n;
    private volatile boolean type = true;//共享变量true foo,false bar
    private final Object o = new Object(); //锁对象
    public FooBar3(int n) {
        this.n = n;
    }

    public  void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            synchronized(o){
                if(!type){
                    o.wait();
                }
                printFoo.run();
                type= false;
                o.notifyAll();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            synchronized(o){
                if(type){
                    o.wait();
                }
                printBar.run();
                type= true;
                o.notifyAll();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        FooBar3 fooBar3 = new FooBar3(4);
        Thread thread = new Thread(()-> {
            try {
                fooBar3.foo(()-> System.out.print("foo"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread1 = new Thread(()-> {
            try {
                fooBar3.bar(()-> System.out.print("bar"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread1.start();
    }
}


/**
 * 方式五：CyclicBarrier 控制先后
 */
class FooBar4{
    private int n;
    CyclicBarrier cyclicBarrier= new CyclicBarrier(2);
    volatile boolean fin = true;
    public FooBar4(int n) {
        this.n = n;
    }

    public  void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            while(!fin);//自旋
            fin=false;
            printFoo.run();
            try {
                cyclicBarrier.await();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }

        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            try {
                cyclicBarrier.await();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            printBar.run();
            fin=true;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        FooBar4 fooBar4 = new FooBar4(4);
        Thread thread = new Thread(()-> {
            try {
                fooBar4.foo(()-> System.out.print("foo"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread thread1 = new Thread(()-> {
            try {
                fooBar4.bar(()-> System.out.print("bar"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        thread1.start();
    }
}



